//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <dma.h>
#include "floppy.h"

STATIC void CDECL FloppyDriverIsr(irq_t irq, void *pvDevice,
                InterruptContext *pContext);

uint_t LBA2CHS(uint_t uLBANum)
{
    uint_t uHead, uDriver, uTrack, uSector;
    uHead = uDriver = uTrack = uSector = 0;

    if (uLBANum >= 1440) {
        uHead = 1;
        uLBANum -= 1440;
    }

    uTrack = uLBANum / 18;
    uSector = uLBANum % 18 + 1;

    return (uHead << 18) + (uDriver << 16) + (uTrack << 8) + uSector;
}

ECode FloppyDriver::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ EzByteBuf buffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    uint_t  uSectorPosition = u64Offset >> 9;

    if (uSectorPosition + (bytesToRead >> 9) > m_uSectors) {
        return E_INVALID_ARGUMENT;
    }

    buffer.SetUsed(0);
    open_motor();

    while (bytesToRead > 0) {
        if (!read_floppy(1, LBA2CHS(uSectorPosition), m_vaDMABuffer)) {
            close_motor();
            return E_FAIL;
        }
        uSectorPosition++;
        bytesToRead -= 512;
        buffer.Append(m_vaDMABuffer, 512);
        //<copy DMABuffer to buffer>
    }
    close_motor();
    return NOERROR;
}

ECode FloppyDriver::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ EzByteBuf buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    uint_t  uSectorPosition = u64Offset >> 9;
    uint_t  uSizeToWritten = buffer.GetUsed();

    if (uSectorPosition + (uSizeToWritten >> 9) > m_uSectors) {
        return E_INVALID_ARGUMENT;
    }

    char *  addr = (char *)buffer;
    * pBytesWritten = 0;

    open_motor();

    while (uSizeToWritten > 0) {
        //<copy buffer to DMABuffer>
        memcpy(m_vaDMABuffer, addr, 512);
        if (!write_floppy(1, LBA2CHS(uSectorPosition), m_vaDMABuffer)) {
            close_motor();
            return E_FAIL;
        }
        uSectorPosition++;
        uSizeToWritten -= 512;
        * pBytesWritten += 512;
        addr += 512;
    }

    close_motor();
    return NOERROR;
}

ECode FloppyDriver::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ EzByteBuf inBuffer,
    /* [out] */ EzByteBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    // reserve for change the inner arguments in Floppy Driver
    return E_NOT_IMPLEMENTED;
}

void FloppyDriver::Dispose()
{
    delete this;
}

ECode FloppyDriver::Initialize()
{
    DzRegisterIsr(FLOPPYDRIVER_IRQ, 0, &FloppyDriverIsr, this);
    m_vaDMABuffer = DzAllocDmaPage();
    kprintf("Alloc virtual addr is 0x%x\n", m_vaDMABuffer);
    *(uint32_t *)m_vaDMABuffer = 0xFFFFFFFF;
//    m_paDMABuffer = VIRTADDR(m_paDMABuffer);

    m_uHeads = FLOPPY_HEADS;            //2
    m_uTracks = FLOPPY_TRACKS;          //80 tracks
    m_uSectorsPerTracks = FLOPPY_SECTORSPERTRACKS;  //18 sectors/trsck
    m_uSectors = m_uHeads * m_uTracks * m_uSectorsPerTracks;    //2880 sectors

    peFloppy = new DzEvent(true, UNSIGNALED);

    kprintf("Floppy Driver Initialize.....\n");

    reset_floppy();

    recalibrate_seek(false);

    recalibrate_seek(true);

    close_motor();

    kprintf("OK!");

    return NOERROR;
}

EXTERN IDeviceDriver * CDECL CreateFloppyDriver(uint_t uDeviceNo, void *pvParameter)
{
    FloppyDriver *pFloppyDriver = new FloppyDriver();
    if (NULL == pFloppyDriver) return NULL;

    if (FAILED(pFloppyDriver->Initialize())) {
        delete pFloppyDriver;
        return NULL;
    }

    pFloppyDriver->AddRef();
    return pFloppyDriver;
}

STATIC void CDECL FloppyDriverIsr(irq_t irq, void *pvDevice,
    InterruptContext *pContext)
{
    assert(pvDevice);
    ((FloppyDriver *)pvDevice)->Isr();
}

INLINE void FloppyDriver::Isr()
{
//    kprintf("Enter FD Isr. \n");
    uint8_t u8Status = 0x00;

    switch (u8Action) {
        case ACTION_RECA:
            u8Status = read_interrupt_status();
            break;
        case ACTION_SEEK:
            u8Status = read_interrupt_status();
            peFloppy->NotifyByIsr(SIGNALED);
            break;
        case ACTION_READ:
            //kprintf("Read interrupt!\n");
            for (int i = 0; i < 7; i++) {
                FD_CMD_DELAY;
                u8Status = Inb(FD_DATA_REGISTER);
                //kprintf("ST%d is 0x%x\n", i, u8Status);
            }
            peFloppy->NotifyByIsr(SIGNALED);
            break;
        case ACTION_WRITE:
            //kprintf("Write interrupt!\n");
            for (int i = 0; i < 7; i++) {
                FD_CMD_DELAY;
                u8Status = Inb(FD_DATA_REGISTER);
                //kprintf("ST%d is 0x%x\n", i, u8Status);
            }
            peFloppy->NotifyByIsr(SIGNALED);
            break;
        case ACTION_DOR:
            u8Status = read_interrupt_status();
            break;
        case ACTION_RESET:
            read_interrupt_status();
            peFloppy->NotifyByIsr(SIGNALED);
            break;
        default:
            kprintf("Unexpected action type.");
    }

    if (u8Status & ST0_INTR) {
        kprintf("Interrupt for error!0x%x\n", u8Status);
        kprintf("main status is 0x%x\n", get_main_status());
    }

    u8Action = ACTION_NOACTION;
    return;
}
